﻿using System;
using System.Collections.Generic;
using System.Windows.Threading;
using DesignByContract;
using MadMediaManager.WPFSupport.Interfaces.Services;
using MadMediaManager.WPFSupport.Interfaces.Services.Threading;
using MadMediaManager.WPFSupport.Interfaces.ViewModels;

namespace MadMediaManager.WPFSupport.Services
{
    public class EventPublisher : IEventPublisher
    {
        private readonly static Dictionary<Type, List<Action<object>>> Subscribers = new Dictionary<Type, List<Action<object>>>();
        private readonly IDispatcher _dispatcher;

        public EventPublisher(IDispatcher dispatcher)
        {
            _dispatcher = dispatcher;
        }

        public void Publish<T>(T eventToPublish)
        {
            Check.Require(eventToPublish != null, "eventToPublish cannot be null");
            List<Action<object>> actions;
            lock (Subscribers)
            {
                if (Subscribers.TryGetValue(typeof(T), out actions) == false)
                {
                    return;
                }
            }
            actions.ForEach(action => _dispatcher.Invoke(action, eventToPublish));
        }

        public void Subscribe<T>(Action<T> action)
        {
            Check.Require(action != null, "action cannot be null");
            Check.Require(action.Target is ISubscriber, "Subscribers must implement ISubscriber");

            lock (Subscribers)
            {
                List<Action<object>> value;
                if (Subscribers.TryGetValue(typeof(T), out value) == false)
                {
                    Subscribers[typeof(T)] = value = new List<Action<object>>();
                }
                Action<object> item = o => action((T) o);
                ((ISubscriber)action.Target).UnsubscribeOnDispose += () => Unsubscribe<T>(item);
                value.Add(item);
            }
        }

        private static void Unsubscribe<T>(Action<object> action)
        {
            lock (Subscribers)
            {
                List<Action<object>> value;
                if (Subscribers.TryGetValue(typeof(T), out value))
                {
                    value.Remove(action);
                }
                
            }
        }
    }
}
